package yb.ecp.fast.user.service;

import com.alibaba.cloud.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import yb.ecp.fast.feign.FastGenClient;
import yb.ecp.fast.infra.infra.log.LogHelper;
import yb.ecp.fast.infra.security.CryptoUtil;
import yb.ecp.fast.infra.util.Ref;
import yb.ecp.fast.infra.util.StringUtil;
import yb.ecp.fast.user.dao.entity.AccountDO;
import yb.ecp.fast.user.dao.entity.AccountLoginRecordDO;
import yb.ecp.fast.user.dao.entity.PasswordDO;
import yb.ecp.fast.user.dao.entity.PasswordDOKey;
import yb.ecp.fast.user.dao.entity.ProfileDO;
import yb.ecp.fast.user.dao.mapper.AccountMapper;
import yb.ecp.fast.user.dao.mapper.PasswordMapper;
import yb.ecp.fast.user.dao.mapper.ProfileMapper;
import yb.ecp.fast.user.exception.AuthException;
import yb.ecp.fast.user.infra.AuthConstant;
import yb.ecp.fast.user.infra.ErrorCode;
import yb.ecp.fast.user.manager.AuthInfoManager;
import yb.ecp.fast.user.service.VO.AccountPwdVO;
import yb.ecp.fast.user.service.VO.AccountVO;
import yb.ecp.fast.user.service.VO.PasswordResetVO;
import yb.ecp.fast.user.service.VO.PasswordVO;
import yb.ecp.fast.user.service.VO.TemplateVO;
import yb.ecp.fast.user.service.VO.UpdatePasswordVO;
import yb.ecp.fast.user.service.VO.UserLoginVO;
import yb.ecp.fast.user.service.auth.handler.AccountHandler;

import javax.servlet.http.HttpServletRequest;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by john on 2017/10/29.
 */
@Service
public class AccountService {
    @Autowired
    FastGenClient genClient;

    @Autowired
    AccountMapper accountMapper;

    @Autowired
    ProfileService profileService;

    @Autowired
    PasswordMapper passwordMapper;

    @Autowired
    ProfileMapper profileMapper;

    @Autowired
    AuthInfoManager authInfoManager;

    @Autowired
    TemplateService templateService;
    @Autowired
    List<AccountHandler> accountHandlerList;

    @Autowired
    AsyncService asyncService;
    @Value("${login.recordIp.default.flag:N}")
    private String loginRecordIp;

    @Value("${role.admin.password:SgcX!515}")
    private String defaultPassword;

    public static String cryptoPassword(String text, String salt) throws Exception {
        String orginalText = text + "_" + salt;
        byte[] cypherBytes = CryptoUtil.encryptMD5(orginalText.getBytes());
        String cypherText = new BigInteger(cypherBytes).toString(16);
        return cypherText;

    }

    /**
     * 登录系统
     *
     * @param userLoginVO UserLoginVO
     * @param userIdRef   Ref<String>
     * @param site        Integer
     * @return ErrorCode
     * @throws Exception Exception
     */
    public ErrorCode login(UserLoginVO userLoginVO, Ref<String> userIdRef, Integer site, HttpServletRequest request, Integer type) throws Exception {
        if (StringUtil.isNullOrSpace(userLoginVO.getCode())) {
            LogHelper.error("验证码为空！", ErrorCode.FailedToVerifyCode.getCode());
            return ErrorCode.FailedToVerifyCode;
        }
        String verifyCode = authInfoManager.getVerifyCode(userLoginVO.getAuthId());
        if (!userLoginVO.getCode().equalsIgnoreCase(verifyCode)) {
            LogHelper.error("验证码错误！", ErrorCode.FailedToVerifyCode.getCode());
            return ErrorCode.FailedToVerifyCode;
        }
        return loginWithoutVerifyCode(userLoginVO, userIdRef, site);

    }


    /**
     * 不校验验证码登陆系统
     *
     * @param userLoginVO UserLoginVO
     * @param userIdRef   Ref<String>
     * @param site        Integer
     * @return ErrorCode
     * @throws Exception Exception
     */
    public ErrorCode loginWithoutVerifyCode(UserLoginVO userLoginVO, Ref<String> userIdRef, Integer site) throws Exception {
        if (accountHandlerList.size() > 0) {
            AccountHandler accountHandler = accountHandlerList.stream().filter(a -> a.isSupport(site)).findFirst().orElse(null);
            if (accountHandler == null) {
                throw new RuntimeException("未匹配到登录方式");
            }
            AccountDO accountDO = accountHandler.handle(userLoginVO, site);
            //校验用户是否可以登录该终端（site）关联企业被删除，用户将无法登陆)
            if (!this.checkUserOnSite(accountDO.getUserId(), site)) {
                LogHelper.error(ErrorCode.NoPermissionForThisSite.getDesc(), ErrorCode.CheckLoginFailure.getCode());
                return ErrorCode.NoPermissionForThisSite;
            }
            //校验用户是否可以登录该终端（site）关联企业被删除，用户将无法登陆)
            if (!this.checkUserOnSite(accountDO.getUserId(), site)) {
                LogHelper.error(ErrorCode.NoPermissionForThisSite.getDesc(), ErrorCode.CheckLoginFailure.getCode());
                return ErrorCode.NoPermissionForThisSite;
            }
            accountMapper.updateLoginTime(accountDO.getUserId());
            userIdRef.set(accountDO.getUserId());
            return ErrorCode.Success;
        }
        throw new RuntimeException("可登录方式为空");
    }


    public void recordUserLoginRecord(HttpServletRequest request, String userId, String loginName) {
        try {
            String ipAddr = getIpAddr(request);
            AccountLoginRecordDO recordDO = new AccountLoginRecordDO();
            recordDO.setLoginIp(ipAddr);
            recordDO.setLoginTime(new Date());
            recordDO.setUserId(userId);
            recordDO.setUserLoginName(loginName);
//            accountLoginRecordMapper.insert(recordDO);
        } catch (Exception e) {
            LogHelper.error("保存用户登录记录失败" + e, ErrorCode.SqlSyntaxError.getCode());
        }
    }

    private String getIpAddr(HttpServletRequest request) {
        try {
            String ip = request.getHeader("x-forwarded-for");
            LogHelper.monitor("x-forwarded-for ip: " + ip);
            if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
                // 多次反向代理后会有多个ip值，第一个ip才是真实ip
                if (ip.indexOf(",") != -1) {
                    ip = ip.split(",")[0];
                }
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
                LogHelper.monitor("Proxy-Client-IP ip: " + ip);
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
                System.out.println("WL-Proxy-Client-IP ip: " + ip);
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
                LogHelper.monitor("HTTP_CLIENT_IP ip: " + ip);
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
                LogHelper.monitor("HTTP_X_FORWARDED_FOR ip: " + ip);
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("X-Real-IP");
                LogHelper.monitor("X-Real-IP ip: " + ip);
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
                LogHelper.monitor("getRemoteAddr ip: " + ip);
            }
            LogHelper.monitor("获取客户端ip: " + ip);
            return ip;
        } catch (Exception e) {
            LogHelper.error("获取用户的ip出现异常" + e, ErrorCode.Failure.getCode());
        }
        LogHelper.error("未获取到ip", ErrorCode.Failure.getCode());
        return null;
    }

    /**
     * 校验用户和当前所登录的终端是否匹配<br/>
     * 匹配通过则返回TRUE
     *
     * @param userId String
     * @param site   Integer
     * @return boolean
     */
    public boolean checkUserOnSite(String userId, Integer site) {
        if (site != 20) {
            TemplateVO templateVO = templateService.queryTempByUserId(userId);

            return null != templateVO && site == templateVO.getSite();
        }
        return true;
    }


    /**
     * 增加一个登录名和密码
     *
     * @param accountPwdVO AccountPwdVO
     * @return ErrorCode
     * @throws Exception Exception
     */
    public ErrorCode addAccountPwd(AccountPwdVO accountPwdVO) throws Exception {
        ErrorCode ret = addAccount(accountPwdVO);
        if (ret != ErrorCode.Success) {
            LogHelper.error(ret.getDesc(), ret.getCode());
            return ret;
        }
        ErrorCode insertRet = insertPassword(accountPwdVO);
        if (insertRet != ErrorCode.Success) {
            LogHelper.error(insertRet.getDesc(), insertRet.getCode());
            return insertRet;
        }
        return ErrorCode.Success;
    }


    /**
     * 增加一个登录名
     *
     * @param accountVO AccountVO
     * @return ErrorCode
     */
    public ErrorCode addAccount(AccountVO accountVO) {
        int ret = accountMapper.checkByPrimaryKey(accountVO.getLoginName());
        if (ret > 0) {
            LogHelper.error(ErrorCode.UserNameExists.getDesc(), ErrorCode.UserNameExists.getCode());
            return ErrorCode.UserNameExists;
        }
        AccountDO accountDO = new AccountDO();
        accountDO.setUserId(accountVO.getUserId());
        accountDO.setLoginName(accountVO.getLoginName());
        accountDO.setStatus(accountVO.getStatus());
        accountDO.setLastLoginTime(null);

        ret = accountMapper.insertSelective(accountDO);
        if (ret <= 0) {
            LogHelper.error("新增账号失败！", ErrorCode.FailedToInsertRecord.getCode());
            return ErrorCode.FailedToInsertRecord;
        }
        return ErrorCode.Success;
    }

    /**
     * 删除一个登录名
     *
     * @param loginName String
     * @return ErrorCode
     */
    public ErrorCode removeAccount(String loginName) {
        AccountDO accountDO = accountMapper.selectByPrimaryKey(loginName);
        if (accountDO == null) {
            LogHelper.error("目标数据不存在！", ErrorCode.Success.getCode());
            return ErrorCode.Success;
        }
        int ret = accountMapper.deleteByPrimaryKey(loginName);
        if (ret <= 0) {
            LogHelper.error("删除登录账号失败！", ErrorCode.FailedToRemoveRecord.getCode());
            return ErrorCode.FailedToRemoveRecord;
        }
        ret = accountMapper.checkByUserId(accountDO.getUserId());
        if (ret > 0) {
            return ErrorCode.Success;
        }
        removePassword(accountDO.getUserId(), 0);
        return ErrorCode.Success;
    }

    /**
     * 根据用户的userId,删除一个用户的所有登录名
     *
     * @param userId String
     * @return ErrorCode
     */
    public ErrorCode removeAccountByUserId(String userId) {
        if (ErrorCode.Success != rmAccountByUserId(userId)) {
            LogHelper.error("删除用户登录账户失败！", ErrorCode.FailedToRemoveRecord.getCode());
            return ErrorCode.FailedToRemoveRecord;
        }
        return removePassword(userId, 0);
    }

    /**
     * 去除一个用户账号
     *
     * @param userId String
     * @return ErrorCode
     */
    public ErrorCode rmAccountByUserId(String userId) {
        accountMapper.deleteByUserId(userId);
        return ErrorCode.Success;
    }

    /**
     * 删除一个用户的密码特定类型密码
     *
     * @param userId String
     * @param type   Integer
     * @return ErrorCode
     */
    public ErrorCode removePassword(String userId, Integer type) {
        PasswordDOKey passwordDOKey = new PasswordDOKey();
        passwordDOKey.setUserId(userId);
        passwordDOKey.setType(type);
        passwordMapper.deleteByPrimaryKey(passwordDOKey);

        return ErrorCode.Success;
    }

    //删除一个用户的所有密码
    public ErrorCode removeAllPassword(String userId) {
        return removePassword(userId, 0);
    }


    //插入一个密码
    private ErrorCode insertPassword(PasswordDO passwordDO) throws Exception {
        int ret = passwordMapper.checkByPrimaryKey(passwordDO);
        if (ret > 0) {
            LogHelper.error(ErrorCode.PwdAlreadSet.getDesc(), ErrorCode.PwdAlreadSet.getCode());
            return ErrorCode.PwdAlreadSet;
        }

        String cypherPassword = cryptoPassword(passwordDO.getPassword(), passwordDO.getUserId());
        passwordDO.setPassword(cypherPassword);
        ret = passwordMapper.insert(passwordDO);
        if (ret <= 0) {
            LogHelper.error("添加用户密码信息失败！", ErrorCode.FailedToInsertRecord.getCode());
            return ErrorCode.FailedToInsertRecord;
        }
        return ErrorCode.Success;

    }

    //插入一个密码
    public ErrorCode insertPassword(PasswordVO passwordVO) throws Exception {

        PasswordDO passwordDO = new PasswordDO();
        passwordDO.setUserId(passwordVO.getUserId());
        passwordDO.setType(passwordVO.getType());
        passwordDO.setPassword(passwordVO.getPassword());
        return insertPassword(passwordDO);

    }


    //插入一个密码
    public ErrorCode insertPassword(AccountPwdVO accountPwdVO) throws Exception {

        PasswordDO passwordDO = new PasswordDO();
        passwordDO.setUserId(accountPwdVO.getUserId());
        passwordDO.setType(accountPwdVO.getType());
        passwordDO.setPassword(accountPwdVO.getPassword());
        return insertPassword(passwordDO);
    }

    /**
     * 批量重置密码
     *
     * @param userIds List
     * @return ErrorCode
     * @throws Exception Exception
     */
    public ErrorCode resetPassword(List<String> userIds) throws Exception {
        List<String> faildList = new ArrayList<>();
        for (String userid : userIds) {
            ErrorCode ee = updatePassword(userid, defaultPassword);
            if (0 != ee.getCode()) {
                faildList.add(userid);
            }
        }
        if (!faildList.isEmpty()) {
            return ErrorCode.FailedToUpdateRecord;
        }

        return ErrorCode.Success;
    }

    /**
     * @Des 修改密码
     * @Date 2021/4/25 10:21
     * @Author wangzhaoyu
     * @Param [passwordReset]
     * @Return yb.ecp.fast.user.infra.ErrorCode
     */
    public ErrorCode modifyPassword(PasswordResetVO passwordReset) throws Exception {
        ErrorCode errorCode = passwordReset.checkParamSubmit();
        if (!ErrorCode.isSuccess(errorCode)) {
            return errorCode;
        }
        //根据用户名和新密码查询用户是否存在
        AccountDO accountDO = accountMapper.selectByPrimaryKey(passwordReset.getLoginName());
        if (accountDO == null) {
            LogHelper.error("登录账号不存在！", ErrorCode.CheckLoginFailure.getCode());
            return ErrorCode.CheckLoginFailure;
        }
        String userId = accountDO.getUserId();
        PasswordDOKey passwordDOKey = new PasswordDOKey();
        passwordDOKey.setUserId(userId);
        passwordDOKey.setType(0);
        PasswordDO passwordDO = passwordMapper.selectByPrimaryKey(passwordDOKey);
        if (passwordDO == null) {
            LogHelper.error("登录密码不存在！", ErrorCode.CheckLoginFailure.getCode());
            return ErrorCode.CheckLoginFailure;
        }

        ProfileDO profileDO = profileMapper.selectById(userId);
        if (profileDO == null || profileDO.getLocked() == AuthConstant.USER_LOCKED) {
            LogHelper.error("用户状态被锁！", ErrorCode.UserLocked.getCode());
            return ErrorCode.UserLocked;
        }
        String oldPassword = passwordReset.getOldPassword();
        String cypherPassword = cryptoPassword(oldPassword, userId);

        if (!cypherPassword.equals(passwordDO.getPassword())) {
            LogHelper.error("原登录密码错误！", ErrorCode.OldPwdCanNotBeNullError.getCode());
            return ErrorCode.OldPwdCanNotBeNullError;
        }
        String newPassword = passwordReset.getNewPassword();
        return updatePassword(userId, newPassword);
    }

    public ErrorCode updatePassword(String userNo, String password) throws Exception {
        PasswordDO passwordDO = new PasswordDO();
        passwordDO.setUserId(userNo);

        String cypherPassword = cryptoPassword(password, userNo);
        passwordDO.setPassword(cypherPassword);
        passwordDO.setType(AuthConstant.PASSWORD_TYPE_LOGIN);
        int ret = passwordMapper.updateByPrimaryKey(passwordDO);
        if (ret <= 0) {
            return ErrorCode.FailedToUpdateRecord;
        }
        return ErrorCode.Success;
    }

    public ErrorCode updatePassword(String userId, UpdatePasswordVO updatePasswordVO) throws Exception {

        PasswordDOKey passwordDOKey = new PasswordDOKey();
        passwordDOKey.setUserId(userId);
        passwordDOKey.setType(AuthConstant.PASSWORD_TYPE_LOGIN);
        PasswordDO passwordDO = passwordMapper.selectByPrimaryKey(passwordDOKey);
        if (passwordDO == null) {
            LogHelper.error("原密码信息不存在！", ErrorCode.OldPwdNotRight.getCode());
            return ErrorCode.OldPwdNotRight;
        }

        String cypherPassword = cryptoPassword(updatePasswordVO.getOldPassword(), userId);

        if (!cypherPassword.equals(passwordDO.getPassword())) {
            LogHelper.error(ErrorCode.OldPwdNotRight.getDesc(), ErrorCode.OldPwdNotRight.getCode());
            return ErrorCode.OldPwdNotRight;
        }
        return updatePassword(userId, updatePasswordVO.getNewPassword());

    }

    /**
     * 修改登录名之前检查登录名是否存在
     *
     * @param accountVO
     * @return
     */
    public ErrorCode updateLoginName(AccountVO accountVO) {
        AccountDO accountDO = new AccountDO();
        accountDO.setUserId(accountVO.getUserId());
        accountDO.setLoginName(accountVO.getLoginName());
        int err = accountMapper.checkLoginName(accountDO);
        if (0 != err) {
            LogHelper.error(ErrorCode.UserNameExists.getDesc(), ErrorCode.UserNameExists.getCode());
            return ErrorCode.UserNameExists;
        }

        ErrorCode rmCode = rmAccountByUserId(accountVO.getUserId());
        if (ErrorCode.Success != rmCode) {
            return rmCode;
        }
        accountVO.setStatus(0);
        ErrorCode addCode = addAccount(accountVO);
        return addCode;
    }

    /**
     * 查询用户登录名
     */
    public AccountVO queryAccountById(String userId) {
        AccountDO accountDO = accountMapper.selectByUserId(userId);
        AccountVO accountVO = getVO(accountDO);
        if (null != accountVO) {
            accountVO.setLastLoginTime(accountMapper.getLastLoginTime(userId));
        }
        return accountVO;
    }

    /**
     * 获取用户所有登录名<br>
     * 一个用户可以拥有多个登录名
     *
     * @param userId String
     * @return List<AccountVO>
     */
    public List<AccountVO> queryAccountsByUserId(String userId) {
        List<AccountDO> accountDOs = accountMapper.selectListByUserId(userId);

        List<AccountVO> accountVOs = new ArrayList<>();

        for (AccountDO accountDO : accountDOs) {
            accountVOs.add(getVO(accountDO));
        }
        return accountVOs;
    }

    private AccountVO getVO(AccountDO accountDO) {
        if (null == accountDO) {
            return null;
        }
        AccountVO accountVO = new AccountVO();
        accountVO.setLoginName(accountDO.getLoginName());
        accountVO.setStatus(accountDO.getStatus());
        accountVO.setUserId(accountDO.getUserId());
        accountVO.setLastLoginTime(accountDO.getLastLoginTime());
        return accountVO;
    }

    /**
     * 根据登录名查询用户ID
     *
     * @param loginName 登录名
     * @return String  用户ID
     */
    public String queryUserIdByAccount(String loginName) {
        return accountMapper.selectUserIdByAccount(loginName);
    }

    /**
     * 检查登录名是否存在
     *
     * @param loginName
     * @return
     */
    public ErrorCode checkLoginName(String loginName) {
        AccountDO accountDO = new AccountDO();
        accountDO.setUserId("");
        accountDO.setLoginName(loginName);
        int err = accountMapper.checkLoginName(accountDO);
        if (0 != err) {
            return ErrorCode.UserNameExists;
        }
        return err == 0 ? ErrorCode.Success : ErrorCode.UserNameExists;
    }

    public ErrorCode checkExistAccountName(String loginName) {
        int ret = accountMapper.checkByPrimaryKey(loginName);
        if (ret > 0) {
            LogHelper.error(ErrorCode.UserNameExists.getDesc(), ErrorCode.UserNameExists.getCode());
            return ErrorCode.UserNameExists;
        }
        return ErrorCode.Success;
    }

    public AccountPwdVO getAccountByName(String username) {
        AccountDO accountDO = accountMapper.selectByPrimaryKey(username);
        if (accountDO == null) {
            return null;
        }
        AccountPwdVO accountPwdVO = new AccountPwdVO();
        PasswordDOKey passwordDOKey = new PasswordDOKey();
        passwordDOKey.setUserId(accountDO.getUserId());
        passwordDOKey.setType(0);
        PasswordDO password = passwordMapper.selectByPrimaryKey(passwordDOKey);
        if (password == null) {
            return null;
        }
        accountPwdVO.setUserId(accountDO.getUserId());
        accountPwdVO.setPassword(password.getPassword());
        accountPwdVO.setLoginName(username);
        return accountPwdVO;
    }

    @Transactional
    public void resetAdminPasswordByOrgId(String orgId) {
        if (StringUtils.isBlank(orgId)) {
            throw new AuthException("参数错误");
        }
        //query all admin user by orgId
        List<AccountDO> list = accountMapper.queryAdminUserByOrgId(orgId);
        if (CollectionUtils.isEmpty(list)) {
            throw new AuthException("查询该企业下管理员账户为空");
        }
        //reset password
        for (AccountDO a : list) {
            String cypherPassword = null;
            try {
                updatePassword(a.getUserId(), defaultPassword);
            } catch (Exception e) {
                throw new AuthException(e.getMessage());
            }
        }
    }
}
